<!--Panorama viewer pannellum wrap. 
  * 封装一个Vue 3.0版pannellum , npm上的 vue-pannellum 仅用于Vue2.x !!
-->
<template>
  <div
    id="vue-pannellum"
    @mouseup="onMouseUp"
    @touchmove="onTouchMove"
    @touchend="onTouchEnd"
  >
    <div class="info">{{ info }}</div>
    <div class="default-slot">
      <slot></slot>
    </div>
  </div>
</template>

<script lang="ts">
import 'pannellum';
import 'pannellum/build/pannellum.css';
import { debounce } from 'lodash-es';
import { computed, watch, defineComponent, 
  ref, toRefs, reactive, onMounted, 
  onBeforeUnmount, nextTick }
from 'vue';

interface State {
  info: string;
}

export default defineComponent({
  name: 'PannellumForVue',
  props: {
    debug: { type: Boolean, default: false },
    src: { type: [String, Object], required: true },
    preview: { type: String, default: '' },
    autoLoad: { type: Boolean, default: true },
    autoRotate: { type: [Number, Boolean], default: 0 },
    orientation: { type: Boolean, default: false },
    draggable: { type: Boolean, default: true },
    mouseZoom: { type: Boolean, default: true },
    doubleClickZoom: { type: Boolean, default: true },
    showInfo: { type: Boolean, default: false },
    showZoom: { type: Boolean, default: false },
    showFullscreen: { type: Boolean, default: false },
    compass: { type: Boolean, default: false },
    hotSpots: { type: Array, default: () => [] },
    hfov: { type: Number, default: 75 },
    minHfov: { type: Number, default: 30 },
    maxHfov: { type: Number, default: 120 },
    yaw: { type: Number, default: 0 },
    pitch: { type: Number, default: 0 },
    crossOrigin: {type: String, default: 'anonymous' },
  },
  emits: ['load', 'error', 'update:hfov', 'update:yaw', 'update:pitch'],
  setup(props, { emit }) {
    const pannellumRef =  ref<HTMLDivElement | null>(null)
    const viewer = ref<any>(null)
    const state = reactive<State>({
      info: ''
    });
    let rafId: any = null;

    const srcOption = computed(() => {
      // console.log("props.src", props.src, typeof props.src)
      if (typeof props.src === 'string') {
          return {
            type: 'equirectangular',
            panorama: props.src,
            hotSpots: props.hotSpots,
          }
      } else if (typeof props.src === 'object') {
          if (props.src.px && props.src.ny) {
            return {
              type: 'cubemap',
              cubeMap: [
                props.src.pz,
                props.src.px,
                props.src.nz,
                props.src.nx,
                props.src.py,
                props.src.ny,
              ],
              hotSpots: props.hotSpots,
            }
          } else if (props.src.scenes) {
            return {
              default: props.src.default,
              scenes: props.src.scenes,
            }
          } else {
            console.error('[vue-pannellum] Unknown src type')
          }
      } else {
        console.error('[vue-pannellum] Unknown src type: ' + typeof props.src)
      }
    })

    watch(
      () => props.src,
      (value) => {
        //this.$el.innerHTML = ''
        nextTick(() => {
          load()
        })
      }
    )
    watch(
      () => props.hfov,
      (value) => {
        if (viewer.value) viewer.value.setHfov(value, false)
      }
    )
    watch(
      () => props.yaw,
      (value) => {
        if (viewer.value) viewer.value.setYaw(value, false)
      }
    )
    watch(
      () => props.pitch,
      (value) => {
        if (viewer.value) viewer.value.setPitch(value, false)
      }
    )
    watch(
      () => props.maxHfov,
      (value) => {
        if (viewer.value) {
          viewer.value.setHfovBounds([props.minHfov, props.maxHfov])
        }
      }
    )
    watch(
      () => props.minHfov,
      (value) => {
        if (viewer.value) {
          viewer.value.setHfovBounds([props.minHfov, props.maxHfov])
        }
      }
    )
    watch(
      () => props.autoRotate,
      (value) => {
      if (value) {
          viewer.value.startAutoRotate()
        } else {
          viewer.value.stopAutoRotate()
          if (props.orientation) viewer.value.startOrientation()
        }
      }
    )
    watch(
      () => props.orientation,
      (value) => {
        if (value) {
          viewer.value.startOrientation()
        } else {
          viewer.value.stopOrientation()
          if (props.autoRotate) viewer.value.startAutoRotate()
        }
      }
    )

    onMounted(() => {
      load()
      rafId = window.requestAnimationFrame(loop)
    });

    onBeforeUnmount(() => {
      viewer.value.destroy()
      window.cancelAnimationFrame(rafId)
    });

    function  load () {
        const srcOps = srcOption.value
        // console.log("srcOps", srcOps)
        let options = {
          autoLoad: props.autoLoad,
          autoRotate: props.autoRotate === true ? -2 : 0,
          orientationOnByDefault: props.orientation,
          draggable: props.draggable,
          mouseZoom: props.mouseZoom,
          doubleClickZoom: props.doubleClickZoom,
          compass: props.compass,
          preview: props.preview,
          hfov: props.hfov,
          yaw: props.yaw,
          pitch: props.pitch,
          minHfov: props.minHfov,
          maxHfov: props.maxHfov,
          crossOrigin: props.crossOrigin,
          // haov: 149.87,
          // vaov: 54.15,
          ...srcOps,
        }
        // console.log('options', options)
        viewer.value = window.pannellum.viewer('vue-pannellum', options)
        viewer.value.on('load', () => {
          emit('load')
        })
        viewer.value.on('error', (err) => {
          emit('error', err)
        })
        if (props.showInfo === false) {
          let el = document.querySelector('.pnlm-panorama-info')
          // Note: Using display will not work when in tour mode and switch scene
          if (el) el.style.visibility = 'hidden'
        }
        if (props.showZoom === false) {
          let el = document.querySelector('.pnlm-zoom-controls')
          if (el) el.style.display = 'none'
        }
        if (props.showFullscreen === false) {
          let el = document.querySelector('.pnlm-fullscreen-toggle-button')
          if (el) el.style.display = 'none'
        }
    }

    function  loop() {
        rafId = window.requestAnimationFrame(loop)
        let hfov = viewer.value.getHfov()
        let yaw = viewer.value.getYaw()
        let pitch = viewer.value.getPitch()
        if (pitch > 90) pitch = 90
        else if (pitch < -90) pitch = -90
        if (hfov != props.hfov) emit('update:hfov', hfov)
        if (yaw != props.yaw) emit('update:yaw', yaw)
        if (pitch != props.pitch) emit('update:pitch', pitch)
    }

    function onMouseUp(){
        if (props.debug) state.info += ' mu'
        debounceRotate()
    }

    function  onTouchMove(){
        if (props.debug) state.info += ' tm'
    }

    function onTouchEnd(){
        if (props.debug) state.info += ' te'
        debounceRotate()
    }

    const  debounceRotate = debounce(function () {
        // priority of orientation is higher
        if (props.orientation) viewer.value.startOrientation()
        else if (props.autoRotate) viewer.value.startAutoRotate()
    }, 3000)

    return {
      pannellumRef,
      ...toRefs(state),
      onMouseUp,
      onTouchMove,
      onTouchEnd
    }
  },
});
</script>
<style lang="less" scoped>
.vue-pannellum {
  width: 100%;
  height: 100%;
  position: relative;
}

.info {
  position: absolute;
  background-color: hsla(0, 0%, 100%, 0.5);
  top: 0;
  left: 0;
  width: 100%;
  z-index: 2;
}

.default-slot {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 2;
}
</style>
<style lang="less">
.pnlm-ui .pnlm-about-msg {
  display: none !important;
}

.pnlm-ui .pnlm-orientation-button {
  display: none !important;
}
</style>
